package com.taglab.chic.taglib.analytics;

import java.io.IOException;

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.tagext.TagSupport;

import org.springframework.util.StringUtils;

/**
 * Google Analytics tag useful for adding user tracking to a page.
 *
 * Recomend putting this as the last tag before &lt;/body&gt;.
 *
 * XXX this is still very early stages, likley to change.
 *
 * @author Markus Kobler
 */
public class GoogleAnalyticsTag extends TagSupport {

  private static final long serialVersionUID = 3416277670012147656L;
  
//  private final static Logger logger = LoggerFactory.getLogger(GoogleAnalyticsTag.class);

  public final static String DEFAULT_TRACKER_NAME = "pageTracker";

  private boolean disabled = false;
  private String trackerName = DEFAULT_TRACKER_NAME;
  private String trackerCode;
  private String urlOverride;
  private Transaction transaction;
  private Event event;

  // Campaign related
  // TODO Add all campaign related keys, should probably use a different tag?
  private String campNameKey;
  private String campMediumKey;
  private String campSourceKey;
  private Boolean allowAnchor;

  public void setTrackerName(String trackerName) {
    // XXX validate for illegal javascript names
    this.trackerName = trackerName.trim();
  }

  public void setTrackerCode(String trackerCode) {
    this.trackerCode = trackerCode.trim();
  }

  public void setUrlOverride(String urlOverride) {
    // XXX validate & escape quoted url
    this.urlOverride = urlOverride.trim();
  }
  
  public void setTransaction(Transaction transaction) {
    this.transaction = transaction;
  }
  
  public void setEvent(Event event) {
    this.event = event;
  }
  
  public void setDisabled(boolean disabled) {
    this.disabled = disabled;
  }

  public void setCampNameKey(String campNameKey) {
    this.campNameKey = campNameKey;
  }

  public void setCampMediumKey(String campMediumKey) {
    this.campMediumKey = campMediumKey;
  }

  public void setCampSourceKey(String campSourceKey) {
    this.campSourceKey = campSourceKey;
  }

  public void setAllowAnchor(Boolean allowAnchor) {
    this.allowAnchor = allowAnchor;
  }

  @Override
  public int doStartTag() throws JspException {
    // Pull overrides out of page context
    boolean disabled = getRequestAttribute(GoogleAnalyticsOverridesTag.DISABLED_VAR, this.disabled);
    String trackerCode = getRequestAttribute(GoogleAnalyticsOverridesTag.TRACKERCODE_VAR, this.trackerCode);
    String trackerName = getRequestAttribute(GoogleAnalyticsOverridesTag.TRACKERNAME_VAR, this.trackerName);
    String urlOverride = getRequestAttribute(GoogleAnalyticsOverridesTag.URLOVERRIDE_VAR, this.urlOverride);
    Transaction transaction = getRequestAttribute(GoogleAnalyticsOverridesTag.TRANSACTION_VAR, this.transaction);
    Event event = getRequestAttribute(GoogleAnalyticsOverridesTag.EVENT_VAR, this.event);

    if(disabled) return SKIP_BODY;

    if( trackerCode == null )
      throw new IllegalStateException("Tracker code not set");

    try {
      JspWriter out = pageContext.getOut();
      out.append("<script type=\"text/javascript\">\n");
      out.append("var gaJsHost = ((\"https:\" == document.location.protocol) ? \"https://ssl.\" : \"http://www.\");\n");
      out.append("document.write(unescape(\"%3Cscript src='\" + gaJsHost + \"google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E\"));\n");
      out.append("</script>\n<script type=\"text/javascript\">\n");
      out.append("try {\n");
      out.append("  var ").append(trackerName).append(" = _gat._getTracker(\"").append(trackerCode).append("\");\n");

      // share for any inner trackerCode
      setValue("trackerName", trackerName);
      setValue("trackerCode", trackerCode);
      if( urlOverride != null ) setValue("urlOverride", urlOverride);
      if( transaction != null ) setValue("transaction", transaction);
      if( event != null ) setValue("event", event);

      return SKIP_BODY;
    } catch (IOException e) {
      throw new JspException(e.toString(), e);
    }
  }


  @Override
  public int doEndTag() throws JspException {
    // Pull overrides out of page context
    boolean disabled = getRequestAttribute(GoogleAnalyticsOverridesTag.DISABLED_VAR, this.disabled);
    String trackerName = getRequestAttribute(GoogleAnalyticsOverridesTag.TRACKERNAME_VAR, this.trackerName);
    String urlOverride = getRequestAttribute(GoogleAnalyticsOverridesTag.URLOVERRIDE_VAR, this.urlOverride);
    Transaction transaction = getRequestAttribute(GoogleAnalyticsOverridesTag.TRANSACTION_VAR, this.transaction);
    Event event = getRequestAttribute(GoogleAnalyticsOverridesTag.EVENT_VAR, this.event);

    String campNameKey = getRequestAttribute(GoogleAnalyticsOverridesTag.CAMP_NAME_KEY, this.campNameKey);
    String campMediumKey = getRequestAttribute(GoogleAnalyticsOverridesTag.CAMP_MEDIUM_KEY, this.campMediumKey);
    String campSourceKey = getRequestAttribute(GoogleAnalyticsOverridesTag.CAMP_SOURCE_KEY, this.campSourceKey);
    Boolean allowAnchor = getRequestAttribute(GoogleAnalyticsOverridesTag.ALLOW_ANCHOR_VAR, this.allowAnchor);

    if(disabled) return SKIP_BODY;

    try {
      JspWriter out = pageContext.getOut();

      if(allowAnchor != null) {
        out.append("  ").append(trackerName);
        out.append("._setAllowAnchor(").append(allowAnchor.toString()).append(");\n");
      }

      if( campNameKey != null ) {
        out.append("  ").append(trackerName);
        out.append("._setCampNameKey('").append(campNameKey).append("');\n");
      }

      if( campMediumKey != null ) {
        out.append("  ").append(trackerName);
        out.append("._setCampMediumKey('").append(campMediumKey).append("');\n");
      }

      if( campSourceKey != null ) {
        out.append("  ").append(trackerName);
        out.append("._setCampSourceKey('").append(campSourceKey).append("');\n");
      }

      out.append("  ").append(trackerName);
      
      out.append("._trackPageview(");
      if( StringUtils.hasText(urlOverride) ) out.append("'").append(urlOverride).append("'");
      out.append(");\n");
      
      if( transaction != null ) {
        out.append("  ").append(trackerName).append("._addTrans(");
        out.append(escapedString(transaction.getOrderId()));
        out.append(escapedString(transaction.getAffiliation()));
        out.append(escapedString(transaction.getTotal()));
        out.append(escapedString(transaction.getTax()));
        out.append(escapedString(transaction.getShipping()));
        out.append(escapedString(transaction.getCity()));
        out.append(escapedString(transaction.getState()));
        out.append(lastEscapedString(transaction.getCountry()));
        out.append(");\n");
        
        for( Item item : transaction.getItems() ) {
          out.append("  ").append(trackerName).append("._addItem(");
          out.append(escapedString(transaction.getOrderId()));
          out.append(escapedString(item.getSku()));
          out.append(escapedString(item.getName()));
          out.append(escapedString(item.getCategory()));
          out.append(escapedString(item.getPrice()));          
          out.append(lastEscapedString(item.getQuantity()));          
          out.append(");\n");
        }
        out.append("  ").append(trackerName).append("._trackTrans();\n");
      }
      
      if( event != null ) {
        
        out.append("  ").append(trackerName).append("._trackEvent(");
        out.append(escapedString(event.getCategory()));
        out.append(escapedString(event.getAction()));
        
        if( event.getLabel() != null ) {
          out.append(escapedString(event.getLabel()));
          out.append(lastEscapedString(event.getValue()));
        } else if( transaction != null ) {
          // By default use the optional label and value for the transaction ID
          out.append(escapedString("transactionId"));
          out.append(lastEscapedString(transaction.getOrderId()));
        }
        out.append(");\n");
      }
      
      out.append("} catch(err) {}\n");      
      out.append("</script>");      
      return EVAL_PAGE;
    } catch (IOException e) {
      throw new JspException(e.toString(), e);
    }
  }
  
  private String escapedString(String value) {
    return lastEscapedString(value)+",";
  }

  private String lastEscapedString(String value) {
    if( value == null ) return "\"\"";
    return "\""+value.replace("\"", "\\\"")+"\"";
  }

  private <T> T getRequestAttribute(String var, T defaultValue) {
    // TODO Add check and logging for ClassCastException
    T value = (T) pageContext.getAttribute(var, PageContext.REQUEST_SCOPE);
    return value == null ? defaultValue : value;
  }
}
